#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/slab.h>


#define ERR(msg) do {	\
	printk("error: %s\n", msg);	\
	printk("error_at: %s:%s %d\n", __FILE__, __func__, __LINE__);	\
} while(0)


ssize_t dev_read (struct file *file, char __user *ubuf, size_t size, loff_t *offset)
{
	printk("%s invoked.\n", __func__);
	return size;
}

ssize_t dev_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offset)
{
	printk("%s invoked.\n", __func__);
	return size;
}

int dev_open (struct inode *inode, struct file *file)
{
	printk("%s invoked.\n", __func__);
	return 0;
}

int dev_close (struct inode *inode, struct file *file)
{
	printk("%s invoked.\n", __func__);
	return 0;
}




struct cdev *cdev_p = NULL;
unsigned int major = 666;
unsigned int minor = 0;
const unsigned int count = 1;
const char *dev_name_p = "demo_dev";
struct class *cls_p;
struct device *dev_p;

const struct file_operations fops = {
	.read 		= dev_read,
	.write 		= dev_write,
	.open 		= dev_open,
	.release 	= dev_close,
};


static int __init demo_init(void)
{
	int ret = 0;
	dev_t devno;

	printk("demo_init invoked.\n");

	// =========== 分步注册字符设备驱动 ===========
	// 1. 申请一个字符设备驱动结构体
	cdev_p = cdev_alloc();

	if (NULL == cdev_p) {
		ERR("cdev_alloc");
		goto ERR1;
	}
	

	// 2. 初始化字符设备驱动结构体
	cdev_init(cdev_p, &fops);
	
	// 3. 申请设备号
	ret = alloc_chrdev_region(&devno, minor, count, dev_name_p);

	if (ret < 0) {
		ERR("alloc_chrdev_region");
		goto ERR2;
	}

	major = MAJOR(devno);
	minor = MAJOR(devno);
	printk("alloc chrdev success major: %d minor: %d\n", major, minor);
	
	// 4. 注册字符设备驱动
	ret = cdev_add(cdev_p, MKDEV(major, minor), count);
	if (ret < 0) {
		ERR("cdev_add");
		goto ERR3;
	}

	// =========== 自动创建设备节点 ===========
	cls_p = class_create(THIS_MODULE, dev_name_p);
	if (IS_ERR(cls_p)) {
		ERR("class_create");
		goto ERR4;
	}
	
	dev_p = device_create(cls_p, NULL, MKDEV(major, minor), NULL, dev_name_p);
	if (IS_ERR(dev_p)) {
		ERR("device_create");
		goto ERR5;
	}

	printk("demo_init success.\n");

	return 0;

ERR5:
	class_destroy(cls_p);

ERR4:
	cdev_del(cdev_p);

ERR3:
	unregister_chrdev_region(MKDEV(major, minor), count);

ERR2:
	kfree(cdev_p);

ERR1:
	return -1;
	
}

static void __exit demo_exit(void)
{
	printk("demo_exit invoked.\n");
	printk("major: %d minor: %d\n", major, minor);
	device_destroy(cls_p, MKDEV(major, minor));
	class_destroy(cls_p);
	cdev_del(cdev_p);
	unregister_chrdev_region(MKDEV(major, minor), count);
	kfree(cdev_p);
	printk("demo_exit success.\n");

}

module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");



